iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 3
0

嗨大家今天過得好嗎?昨天被 MVVM 的 sample code 迷惑了雙眼,一時就切換不了 Paging 如何在 MVP 實作的,今天冷靜下來終於順利加上 Paging 也讓畫面不那麼卡了!

Paging in MVP

昨天說過組成 Paging 的三位一體分別是 PagedList、DataSource 和 PagedListAdapter,原先卡住我的是在 MVVM 的架構中會由 ViewModel 產生一組 DataSource,可以再轉成 LiveData 或是 Observer 提供給 View 訂閱,當 View 的 PagedListAdapter 載入的資料快要見底時,觸發 ViewModel 的 LiveData 或 Observer 再拿下一批資料,MVVM 的實現來自於 View 和 ViewModel 之間存在觀察者模式,Observer 也會跟著 View 的 Lifecycle,但在 MVP 架構呢?

我原先理解的 MVP 架構是由 Presenter 跟 DataSource 拿到資料後再傳給 VIew 去顯示,Presenter 的 Observer 在被訂閱後就會經 onSucess 或是 onError 然後中斷,等到下一次訂閱 observer 時才會再載入下一批資料,但神奇的是 Paging 的架構 Presenter 只要經過一次訂閱,把資料傳給 View 就好,等 View 的資料快要到底時 PagedListAdapter 會通知 DataSource loadAfter 載入下一頁,Presenter 不必再次訂閱 Observer,這和我原先認知的 MVP 做法不太一樣,導致我昨天轉不過來一直試圖要用原來的方法重新訂閱 Observer,才會導致實作不出來。

其實 Paging 不論在 MVP 還是 MVVM 都還是做自己, PagedList、DataSource 和 PagedListAdapter 之間會互通有無,分工更細的 API 就是要簡化讓使用者更方便使用分頁,但我覺得這個防呆設計並沒有比較清楚(也可能是我就呆...),Google 在 2018 推出後也收到各方開發者的回饋,於是今年 2020 升級到了 Paging3 的 Library,據說導入更簡單也可以從原先的 library 轉換過去,但具體情況如何讓我們明天繼續看下去。

簡單附上 psedo code:

// Model
class Song (val id: String)

// DataSource
class PagingDataSource(val apiManager: APIManager) : PageKeyedDataSource<Int, Song>() {

    var page = 0

    override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, Song>) {
        // step1.呼叫 API 載入首頁資料
				// step2-1.載入成功 -> 載入下一頁資料
					page += 1
					callback.onResult(data, previousPaageKey = null, page)
				// step2-2.載入失敗 -> errorHandle
    }

    override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Song>) {
        // step1.呼叫 API 載入次頁資料,剛剛 page 有加一表示換頁了
				// step2-1.載入成功 -> 載入下一頁資料
					page += 1
					callback.onResult(data, previousPaageKey = null, page)
				// step2-2.載入失敗 -> errorHandle
    }

    override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, Song>) {
		    // 沒有換前頁需求就不實作
		}
}

class PagingDataSourceFactory(val apiManager: APIManager) : DataSource.Factory<Int, Song>() {
    override fun create(): DataSource<Int, Song> {
        return PagingDataSource(apiManager)
    }
}

// Presenter
class SongListPresenter(val apiManager: APIManager) {
		
		override fun onAttach(view: V) {
        super.onAttach(view)
        
				val pagedListConfig = PagedList.Config.Builder()
                .setPageSize(20)
                .setPrefetchDistance(4)
                .build()

        RxPagedListBuilder(PagingDataSourceFactory(apiManager), pagedListConfig)
                .setFetchScheduler(Schedulers.io())
                .setNotifyScheduler(AndroidSchedulers.mainThread())
                .buildObservable()
								.compose(RxUtils.schedulerTransformer())
                .subscribe {
                    getView()?.addSongs(it)
                }
    }
}

// Fragment
class SongListFragment() {
		
		override fun addSongs(songs: PagedList<Song>) {
        songListAdapter?.submitList(songs)
    }
}

上一篇
換頁 Paging (1):簡介
下一篇
換頁 Paging (3):新同學 Paging3
系列文
打造一個厲害的普通 Android App - 使用者體驗優化16
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言